Casio make some big chunky watches, the only problem is that the actual display real estate is small compared to the size of the watch.
Casio PRW-3510 |
Garmin FR245 |
Amazfit T-Rex 3 |
![]() |
![]() |
![]() |
Case = 56.9 x 53.4mm
|
Case = 42 x 42mm
|
Case = 48.5 x 48.5mm
|
Finally the T-Rex3 is a rugged watch with a decent sized screen.
All fitness watches make a big deal about heart rate zones and their effect on training. Despite the aggressive marketing wrist based optical Heart Rate sensors have limitations. Mostly they have trouble detecting changes quickly and brief peaks. For steady state cardio a wrist HR sensor is good enough. For HIIT or Weights a chest HR strap provides better metrics.
It gets even worse with Heart Rate Zones. Amazfit Zepp app assumes a maximum HR of (220 - age). So for me this is a Max HR of 166. But after wearing the watch for a couple of months, for my 10km runs Zepp app shows the following metrics:
If these fitness watches and cloud computing are so smart, with all sorts of proprietary algorithms and Artificial Intelligence, why does it show:
Surely given all the data and metrics accumulated about me over the past few months it should automatically adjust my Max HR and HR Zones? But no, each run shows I break the laws of nature. Garmin was just the same.
Despite the agressive marketing wrist based optical Heart Rate sensors have limitations.
Even with accurate Heart Rate monitoring by a Chest Strap not all is as it seems. My empirical evidence of Heart Rate does not match the advice on the internet.
Amazfit has a ZeppOS SDK which is based on JavaScript.
# installs nvm (Node Version Manager) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # download and install Node.js (you may need to restart the terminal) nvm install 20 # verifies the right Node.js version is in the environment node -v # should print `v20.15.0` # verifies the right NPM version is in the environment npm -v # should print `10.7.0` curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash nvm install lts nvm use lts nvm install --lts nvm use --lts npm i @zeppos/zeus-cli -g zeus -v
Install using the instructions above and then we are up an running.
Time to create some Amazfit Apps.
The Amazfit T-Rex 3 has a fantastic OLED display with 2000nits. I do not see the point of buying a watch with such a great screen and then turning it off most of the time.
The problem with Raise to Wake is:
So I wrote my own watchface that has an Always On Display (AOD).
![]() |
![]() |
Full power display. |
Always on Display (AOD). |
Power consumption for Always On Display (AOD) is very important.
It turns out not all colors consume the same amount of power.
With this in mind I automatically adjust the color of my AOD display depending on the LUX value which I check every minute with getLight() API call.
![]() |
![]() |
White - #ffffff
|
Olive - #808000
|
![]() |
![]() |
Dark Green - #006400
|
Dark Red - #8B0000
|
The AOD can be hard to read in direct sunlight Especially with sunglasses on. I really want to enable Raise to Wake is lux > 3000 and turn off if lux < 3000. Unfortunately if you turn off the global Raise to Wake option the onWristMotion() does not fire. However the Accelerometer onChange() does still work.
Interpreting the Accelerometer x,y,z values to determine that the wrist has been raised was quite tricky. The internet is full of very complex mathematical formula, but no simple library or algorithm I could copy or translate to JavaScript. I asked ChatGPT, but it just gave plain wrong answers and code that would not execute.
In the end I found some correct formulas to calculate pitch and roll, then experimented with threshold values that matched my wrist position. I ended up with:
let pitch = 180 * Math.atan2(x, Math.sqrt(y * y + z * z)) / 3.14 let roll = 180 * Math.atan2(y, z) / 3.14 if (pitch > -10 && pitch < 10 && roll < 60 && roll > 30 ) { // Wrist raised - exit AOD screen (back to main WatchFace) }
I averaged the last 10 x,y,z values for a more stable reading. I then use setPageBrightTime() with any positive value to exit the AOD screen.
This solution worked well. It should not take much battery. I only enable and monitor the Accelerometer when lux > 3000 and then disable the Accelerometer when lux < 3000
![]() |
Raise to Wake Full power display > 3000 |
A simple app to store the Parkrun bar and QR codes.
![]() |
![]() |
Parkrun Barcode. |
Parkrun QR Code. |
The default strength workout timers on Smart Watches are rubbish.
So I wrote my own simple workout app with a number of pre-defned timers: 60, 90, 120, 180, 300, 600.
![]() |
![]() |
Timer. 6 preconfigured. |
Heart Rate and timer. |
![]() |
![]() |
Time and Date. |
Elapsed Time. |
Rather than widgets Amazfit T-Rex 3 uses Shortcut Cards. I use these to display information in large text rather than putting them on the main display in small text.
![]() |
![]() |
![]() |
Big Date |
Big steps |
Big battery |
In activities it is possible to add custom data screens.
The big problem with the Amazfit T-Rex 3 Data Screens is that even with a single data field the text is too small. For example the pace screen in the Outdoor Running activity compared to the much smaller Garmin Forerunner 245
![]() |
![]() |
Amazfit T-Rex 3 |
Garmin FR245 |
Look at all that wasted screen space on the T-Rex 3! The smaller Garmin Forerunner 245 with its choice of thicker fonts is more readable.
I find the Amazfit T-Rex 3 too difficult to read when running. Doubly so when its bright sunlight and need to wait for the raise wrist action to light the screen up. The older smaller Garmin Forerunner 245 wins here.
I guess most software is written by youngsters with perfect eyesight. But once you pass 40 years of age close vision gets worse. It's a condition called presbyopia. No one wants to wear their reading glasses while running!
For me Data Screens are probably the most important feature of any sports/smart watch. I have yet to read a review that even mentions them.
Fair play to Amazfit. They listened to feedback, and in version 3.6.5.9 they increased the size of the text:
![]() |
![]() |
![]() |
Original Amazfit Workout Time |
NEW Amazfit Workout Time |
My Workout Time |
Its nearly as good as my workout extension :-)
Thankfully Amazfit T-Rex 3 supports Workout Extensions.
Workout Duration comes from getSportData()
![]() |
![]() |
![]() |
Amazfit Workout Time |
My Workout Time |
Garmin FR245 |
Workout Distance comes from getSportData()
![]() |
![]() |
Amazfit Distance |
My Distance |
Pace is my key running screen. Its important to be able to see this while running at a glance in bright sunlight while wearing shades!
![]() |
![]() |
Amazfit Lap Pace |
My Lap Pace. |
Note the much bigger font. It also shows lap number and progress (green) arc) in lap.
![]() |
![]() |
Amazfit Lap Pace |
My Lap Pace. |
For running (lap pace below 10mins a km) I use an even bigger font, I also enable pauseDropWristScreenOff to keep the display on for easy reading in direct sunlight. Raise to Wake screen is too slow. This does mean a small hit on the battery, but running is the main reason I bought this watch. I lose around 5% (GPS and screen) on an hours run. I can live with this.
Unfortunately getSportData() does not make any lap data available. Also lap data is not exported in GPX files, and the lap alert screen cannt be customised. So I stopped using Amazfit lap feature and calculate the 1km laps myself. This does mean if I navigate away from the lap pace screen when a lap is completed I lose the ability to compute the next lap pace. In this case I resort to average lap pace until the next lap is completed.
I also display a custom lap alert screen.
![]() |
![]() |
Last Lap Pace and Average Pace. |
Last Lap Pace and Time for 5km and 10km. |
I like to see the numerical HR Zone, because each vendor calls them by different names. Also I keep the lap progress (green) arc and lap number.
![]() |
![]() |
Amazfit Heart Rate |
My Heart Rate |
I got a bit fed up with the info displayed by the Zepp app for runs.
So I wrote some javascript to read the exported GPX file and display all the data. I also pull the weather in from https://open-meteo.com/
![]() |
![]() |
![]() |
List runs. Can sort runs by column |
Map, Weather and Lap data |
Heart rate data |
Works for me!